`timescale 1ns/1ps
`default_nettype none

/* NOTE:
*  - 串移数据
*/

module data_shift_mbi5353
    #(
    parameter   DW      = 96
    )
    (
    // system signal
    input  wire         I_sclk,  // 125M
    input  wire         I_rst_n,
    // config
    input  wire         I_enable_5353b,     // 芯片类型
    input  wire [7:0]   I_cfg_clock_low,   // 时钟低电平时钟数
    input  wire [7:0]   I_cfg_clock_cycle, // 时钟整周期时钟数
    input  wire [7:0]   I_cfg_clock_phase, // 时钟相位
    // shift request
    input  wire         I_shift_req,       // 串移开始
    output wire         O_shift_busy,      // 正在串移
    input  wire [9:0]   I_shift_bit_num,   // 串移长度(bit)
    input  wire [4:0]   I_shift_load_num,  // load宽度
    input  wire [DW-1:0] I_shift_data,      // 串移数据
    output wire         O_shift_data_ack,  // 数据确认
    // data out
    output wire         O_load_out,
    output wire         O_clock_out,
    output wire [DW-1:0] O_data_out
);

//------------------------Parameter----------------------
localparam
    L0 = 2,  // 时钟输出延时时间 
    L1 = 1;  // load输出延时时间

//------------------------Local signal-------------------
// clock output
reg  [7:0]  clk_cnt;
reg  [7:0]  clk_cnt_2;
reg         clk_out_5353b;
reg         clk_out;
reg  [L0:0] clk_sr;
reg  [L0:0] clk_sr_5353b;
reg         clk_en;

// data output
reg  [9:0]    data_cnt;
reg  [DW-1:0] data_buf0;
reg  [DW-1:0] data_buf1;
reg           data_ack;

// load output
reg         load_out;
reg  [4:0]  load_cnt;
reg  [7:0]  load_clk_cnt;
reg  [L1:0] load_sr;

//------------------------Instantiation------------------

//------------------------Body---------------------------
//{{{+++++++++++++++++++++clock output+++++++++++++++++++
assign O_clock_out = clk_sr[L0];

// clk_cnt
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        clk_cnt <= 1'b1;
    else if (~clk_en)
        clk_cnt <= 1'b1;
    else if (clk_cnt == I_cfg_clock_cycle)//一个clk_out周期包含的clk时钟数
        clk_cnt <= 1'b1;
    else
        clk_cnt <= clk_cnt + 1'b1;
end

// clk_cnt_2 双沿输出
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        clk_cnt_2 <= 1'b1;
    else if (~clk_en)
        clk_cnt_2 <= 1'b1;
    else if (clk_cnt_2 == {I_cfg_clock_cycle,1'b0})//一个clk_out周期包含的clk时钟数
        clk_cnt_2 <= 1'b1;
    else
        clk_cnt_2 <= clk_cnt_2 + 1'b1;
end
// clk_out
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        clk_out <= 1'b0;
    else if (~clk_en)
        clk_out <= 1'b0;//clk_out复位至0
    else if( I_enable_5353b == 1'b0) begin
        if (clk_cnt == I_cfg_clock_low)//计数至 I_cfg_clock_low 拉高clk_out
            clk_out <= 1'b1;
        else if (clk_cnt== I_cfg_clock_cycle)//clk_out复位至0
            clk_out <= 1'b0;
    end
    else if( I_enable_5353b == 1'b1) begin
        if (clk_cnt_2 == I_cfg_clock_low)
            clk_out <= 1'b1;
        else if (clk_cnt_2 == I_cfg_clock_cycle + I_cfg_clock_low)
            clk_out <= 1'b0;
    end
end
// clk_sr
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        clk_sr <= 1'b0;
    else
        clk_sr <= {clk_sr[L0-1:0], clk_out};//打3拍输出
end
// clk_en
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        clk_en <= 1'b0;
    else if (I_shift_req)
        clk_en <= 1'b1;
    else if (clk_cnt == I_cfg_clock_cycle && data_cnt == 1'b1)//最后1bit移位完成，失能该标志位
        clk_en <= 1'b0;
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++data output++++++++++++++++++++
assign O_data_out = data_buf1;  
assign O_shift_busy = clk_en;
assign O_shift_data_ack = data_ack;

// data_cnt
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        data_cnt <= 1'b0;
    else if (I_shift_req)
        data_cnt <= I_shift_bit_num;//每次移位的bit数为 I_shift_bit_num
    else if (clk_cnt == I_cfg_clock_cycle)//一次移位完成，bit数减1
        data_cnt <= data_cnt - 1'b1;
end

// data_buf0
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        data_buf0 <= 1'b0;
    else if (data_ack)//数据第二拍
        data_buf0 <= I_shift_data;
end

// data_buf1
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        data_buf1 <= 1'b0;
    else
        data_buf1 <= data_buf0;//数据第三拍，与clkout下降沿对齐
end

// data_ack
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        data_ack <= 1'b0;
    else if (clk_en && clk_cnt == I_cfg_clock_phase)//每个clkout拉高一个clk,数据第一拍
        data_ack <= 1'b1;
    else
        data_ack <= 1'b0;
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++load output++++++++++++++++++++
assign O_load_out = load_sr[L1];

// load_out
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        load_out <= 1'b0;
    else if (clk_en && data_cnt == I_shift_load_num && clk_cnt == I_cfg_clock_phase)//data_cnt递减至I_shift_load_num时，拉高load_out 
        load_out <= 1'b1;
    else if (load_cnt == 1'b1 && load_clk_cnt == I_cfg_clock_cycle)
        load_out <= 1'b0;
end

// load_cnt
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        load_cnt <= 1'b0;
    else if (clk_en && data_cnt == I_shift_load_num && clk_cnt == I_cfg_clock_phase)//data_cnt递减至I_shift_load_num时，把I_shift_load_num的值赋给load_cnt
        load_cnt <= I_shift_load_num;//load_cnt比data_cnt慢一拍，所以clk_out打3拍输出，load_out打2拍输出，最后两个信号的下降沿是对齐的
    else if (load_out && load_clk_cnt == I_cfg_clock_cycle)//经过clk_out一个周期的clk计数load_cnt减1
        load_cnt <= load_cnt - 1'b1;
end

// load_clk_cnt
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        load_clk_cnt <= 1'b1;
    else if (clk_en && clk_cnt == I_cfg_clock_phase)//相对于clk_out的相移为 I_cfg_clock_phase
        load_clk_cnt <= 1'b1;
    else if (load_clk_cnt == I_cfg_clock_cycle)//与clk_out计数长度一样长，都是 I_cfg_clock_cycle;复位
        load_clk_cnt <= 1'b1;
    else
        load_clk_cnt <= load_clk_cnt + 1'b1;
end

// load_sr
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        load_sr <= 1'b0;
    else
        load_sr <= {load_sr[L1-1:0], load_out};//打2拍输出
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

endmodule

`default_nettype wire

// vim:set ts=4 sw=4 et fenc=utf-8 fdm=marker:
